package org.example.versii.channel.impl;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.example.codec.RpcDecoder;
import org.example.codec.RpcEncoder;
import org.example.common.RpcRequest;
import org.example.common.RpcResponse;
import org.example.versii.channel.IChannel;
import org.example.versii.server.ServiceMap;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author mojito
 * @Classname NettyChannel
 * @Description TODO
 * @Date 2020/7/21 下午11:15
 **/
@Component("NETTY_CHANNEL")
@Slf4j
public class NettyChannel extends SimpleChannelInboundHandler<RpcRequest> implements IChannel {
    @Override
    public void process(int port) {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup work = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .localAddress(port)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //in
                        pipeline.addLast(new RpcDecoder(RpcRequest.class));
                        pipeline.addLast(new RpcEncoder(RpcResponse.class));
                        pipeline.addLast(new NettyChannel());
                        //out

                    }
                });

        try {
            log.info("server listen at: [{}]", port);
            ChannelFuture channelFuture = bootstrap.bind().sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                boss.shutdownGracefully().sync();
                work.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception {
        RpcResponse response = processRequest(rpcRequest);
        log.info("server response: [{}]", response);
        channelHandlerContext.writeAndFlush(response);
    }

    private RpcResponse processRequest(RpcRequest request) throws Exception {
        log.info("rpc call: [{}]", request);
        String className = request.getClassName();
        Object service = ServiceMap.getService(className);
        Class<?> forName = Class.forName(className);
        Method method = forName.getMethod(request.getMethodName(), request.getParameterTypes());
        RpcResponse invoke = (RpcResponse) method.invoke(service, request.getParams());
        log.info("rpc response: [{}]", invoke);
        return invoke;
    }
}
